#!/usr/bin/python
# REDWraps
# REDLisp C++ Module Wrapper Creator
# Copyright 2006 Rudolf Olah

from string import strip, replace, find
from sys import argv, exit
from os import mkdir
from os.path import exists

def clean (line):
  newline = strip (line)
  count = find (newline, "  ")/2
  i = 0
  while i < count:
    newline = replace (newline, "  ", " ")
    i += 1
  return newline

data_types = {"i":"dNumber", "d":"dNumber", "r":"dNumber",
              "s":"dString", "e":"dError", "S":"dSymbol", "b":"dSymbol",
              "FILE*":"dFile", "mix_chunk*":"dMixChunk",
              "mix_music*":"dMusic"}

convert_var = {"s":"toString ()", "S":"toString ()", "b":"toString ()",
               "file*":"file", "mix_music*":"music", "mix_chunk*":"sample"}

def create_module_load (source, function_names):
  source.write ("\nextern \"C\"\nvoid\nload_module ()\n{\n")
  for function in function_names:
    source.write ("  addToSystem (\"" + function[1] + "\", " + function[0] + ");\n")
  source.write ("}\n")

def call_function (source, return_type, function_name, argument_list):
    if (return_type == "dObject"): # Assume it's a boolean
      source.write ("  if")
    else:
      source.write ("  temp = new " + return_type)
    source.write (" (" + function_name + " (")
    i = 0
    length = len (argument_list)
    if (length > 0):
      while (i < length):
        try:
          source.write (clean (argument_list[i][0]))
        except KeyError:
          try:
            source.write (clean (argument_list[i][0]) + "->")
            source.write (convert_var[argument_list[i][1]])
          except KeyError:
            exit ("Crap...")
        if (i < length-1):
          source.write (", ")
        i += 1
    source.write ("))")
    if (return_type == "dObject"): # Assume it's a boolean
      source.write ("\n    temp = T;\n  else\n    temp = nil")
    source.write (";\n  ")
    source.write ("return temp;\n")
    source.write ("}\n\n")

def check_args (source, redlisp_name, arguments):
  length = len (arguments)
  if (arguments[0] != ""):
    source.write ("  if (args == 0)\n    return new dError(\"" + redlisp_name + ": requires " + str (length) + " argument")
    if (length > 1):
      source.write ("s")
    source.write ("\");\n")
  else:
    return []
  i = 0
  argument_list = []
  for token in arguments:
    token = clean (token)
    arg_type = token[:token.rfind (" ")].lower ()
    arg_name = token[token.rfind (" ")+1:].lower ()
    if (arg_type == "int"):
      arg_type = "i"
    elif (arg_type == "double"):
      arg_type = "d"
    elif (arg_type == "const char*") or (arg_type == "char") or (arg_type == "char*") or (arg_type == "string") or (arg_type == "const char *"):
      arg_type = "s"
    elif (arg_type == "bool"):
      arg_type = "b"
    source.write ("\n  ")
    try:
      source.write (data_types[arg_type])
    except KeyError:
      source.write (arg_type)
    source.write (" *" + arg_name + ";\n")
    source.write ("  if ((*args)[" + str (i) + "]->")
    if (len (arg_type) == 1):
      source.write ("type != \'")
      if (arg_type == "b"):
        source.write ("S\' || (*args)]" + str (i) + "]->type != \'l")
      else:
        source.write (arg_type)
      source.write ("\')\n")
    else:
      source.write ("typeStr != \"" + clean (arg_type.upper ()) + "\")\n")
    source.write ("    return new dError (\"" + redlisp_name + ": argument " + str (i+1) + " must be ")
    if (arg_type == "i"):
      source.write ("an integer")
    elif (arg_type == "d"):
      source.write ("a double")
    elif (arg_type == "s"):
      source.write ("a string")
    elif (arg_type == "b"):
      source.write ("T or nil")

    arg_name = clean (arg_name)
    source.write ("\");\n  " + arg_name + " = (")
    argument_list.append ([arg_name, arg_type])
    try:
      source.write (data_types[arg_type])
    except KeyError:
      source.write (arg_type)
    source.write ("*)(*args)[" + str (i) + "];\n")
    i += 1
  return argument_list

def create_wrapper (header, source, infile):
  header.write ("#include \"types.h\"\n#include \"core.h\"\n")
  line = infile.readline ()
  while line != "\n":
    header.write (line)
    line = infile.readline ()
  header.write ("\nusing namespace std;\n\n")
  header.write ("extern dSymbol *T;\n")
  header.write ("extern dList *nil;\n")
  header.write ("extern \"C\" void load_module ();\n\n")

  function_names = []

  line = infile.readline ()
  while line:
    line = clean (line)
    return_type = clean (line[:line.find (" ")]).lower ()
    function_name = clean (line[line.find (" ")+1:line.find ("(")])
    redlisp_name = clean (line[line.find (";")+1:]).lower ()
    function_names.append (["d" + function_name.capitalize (), redlisp_name])
    # Make the function name for the wrapper function
    red_function = function_name.capitalize () + " (dList *args)"
    header.write ("dObject *d" + red_function + "; // Calls " + line[:line.find (";")] + "\n")
    source.write ("\n/********************************\n")
    source.write (" * Calls " + line[:line.find (";")] + "\n")
    source.write (" ********************************/\n")
    source.write ("dObject *\nd" + red_function + "\n{\n")
    # Handle the return type of the function
    if (return_type == "int") or (return_type == "double"):
      return_type = "dNumber"
    elif (return_type == "char*") or (return_type == "string") or (return_type == "const char*"):
      return_type = "dString"
    elif (return_type == "bool"):
      return_type = "dObject"
    else:
      try:
        return_type = data_types[return_type]
      except KeyError:
        exit ("Crap...")
    source.write ("  " + return_type + " *temp; // " + return_type + " to return\n")
    # Handle the arguments of the function
    arguments = clean (line[line.find ("(")+1:line.find (")")]).split (", ")
    argument_list = check_args (source, redlisp_name, arguments)
    call_function (source, return_type, function_name, argument_list);
    line = infile.readline ()
  create_module_load (source, function_names)



print "REDLisp C++ Module Wrapper Creator\nCopyright 2006 Rudolf Olah"
try:
  print "Opening " + argv[1] + " for conversion..."
except IndexError:
  print "Usage:\n\tredwraps.py [filename]"
  exit ()
infile = open (argv[1], "r")
module_name = clean (infile.readline ())
module_dir = module_name.lower () + "_wrap"
if (not exists (module_dir)):
  mkdir (module_dir)
print "Opening " + module_name.lower () + " for creation..."
header = open (module_dir + "/" + module_name.lower () + ".h", "w")
source = open (module_dir + "/" + module_name.lower () + ".cpp", "w")
header.write ("// " + module_name + "\n// Copyright 2006\n")
header.write ("#ifndef " + module_name.upper () + "\n#define " + module_name.upper () + "\n\n")
source.write ("// " + module_name + "\n// Copyright 2006\n")
source.write ("#include \"" + header.name + "\"\n\n")
create_wrapper (header, source, infile)
header.write ("\n#endif\n")
infile.close ()
header.close ()
source.close ()
print "Files closed\nWrapper template files ready :D"
